package com.vf.cloud.paas.app.master.api.render.controller;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.kit.JsonKit;
import com.jfinal.kit.StrKit;
import com.vf.cloud.paas.app.agent.common.util.KeyUtil;
import com.vf.cloud.paas.app.master.common.domain.Project;
import com.vf.cloud.paas.app.master.common.domain.ProjectSynStatus;
import com.vf.cloud.paas.app.master.common.domain.RenderLog;
import com.vf.cloud.paas.app.master.common.domain.Scene;
import com.vf.cloud.paas.app.master.common.domain.UserAgent;
import com.vf.cloud.paas.app.master.common.vo.render.RenderOrderP;
import com.vf.cloud.paas.common.cirrus.Cirrus;
import com.vf.cloud.paas.common.log.annotation.RLog;
import com.vf.cloud.paas.common.service.IRedis;
import com.vf.cloud.paas.common.util.PrefixUtil;
import com.vf.cloud.paas.common.util.R;
import com.vf.cloud.paas.common.vo.ROrder;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/v1/render")
public class RenderController {
	
	@Value("${master.ip}")
	private String masterIP;
	@Value("${master.ue.port}")
	private String masterUEPort;
	
	@Autowired
	private IRedis redisImpl;

	@RLog(value = "请求渲染",type = "user" )
	@RequestMapping(value = "/order", method = RequestMethod.POST)
	public R<ROrder> AnyOrder(@RequestBody RenderOrderP orderP) {

		if (orderP == null || StrKit.isBlank(orderP.getAppid())) {
			return R.failed("参数必填.");
		}

		Scene scene = Scene.dao.findById(orderP.getAppid());

		if (scene == null) {
			return R.failed("口令无效.");
		}

		List<UserAgent> userAgents = UserAgent.dao.find("select * from " + UserAgent.TABLE_NAME + " where user_id=? ",
				scene.getCreateUserId());
		if (userAgents == null || userAgents.size() == 0) {
			return R.failed("口令未授权渲染资源或已取消.");
		}

		Project project = Project.dao.findById(scene.getProjectId());
		if (project == null) {
			return R.failed("应用已移除.");
		}

		String GPUIndex = "";
		String agentToken = "";
		ProjectSynStatus projectSynStatus;
		for (UserAgent userAgent : userAgents) {
			Set<String> keys = redisImpl.getByUuidPrefix(userAgent.getGpuId());
			for (String key : keys) {
				if (key.startsWith(PrefixUtil.GPU_Free)) {
					agentToken = redisImpl.get(key);
					projectSynStatus = ProjectSynStatus.dao.findByIds(agentToken, project.getId());
					if (projectSynStatus != null && StrKit.equals("3", projectSynStatus.getStatus())) {
						GPUIndex = key;
						break;
					}
				}
			}
		}

		if (StrKit.isBlank(GPUIndex)) {
			return R.failed("资源不足.");
		}

		int index = KeyUtil.getGPUIndex(GPUIndex);
		if (StrKit.isBlank(agentToken) || index == -1) {
			log.info("渲染资源不足." + GPUIndex);
			return R.failed("资源不足.");
		}

		ChannelHandlerContext ctx = Cirrus.AgentManager.get(agentToken);
		if (ctx == null) {
			return R.failed("资源不足.");
		} else {

			redisImpl.delete(GPUIndex);
			redisImpl.delete(String.format("%s_%s", agentToken, GPUIndex));

			String uuid = StrKit.getRandomUUID();
			Map<String, Object> clientConfig = new HashMap<String, Object>();
			clientConfig.put("type", "StartClient");
			clientConfig.put("appid", scene.getId());
			clientConfig.put("projectid", scene.getProjectId());
			clientConfig.put("resX", orderP.getResX());
			clientConfig.put("resY", orderP.getResY());
			clientConfig.put("projectName", project.getName());
			clientConfig.put("exeRelativePath", project.getExeRelativePath());
			clientConfig.put("index", index);
			clientConfig.put("GPUIndex", GPUIndex);
			clientConfig.put("uuid", uuid);
			clientConfig.put("socket", String.format("http://%s:%s", masterIP,masterUEPort));
			

			Cirrus.orders.put(uuid, "");
			String mm = JsonKit.toJson(clientConfig);

			ctx.channel().writeAndFlush(new TextWebSocketFrame(mm)).addListener(new ChannelFutureListener() {
				@Override
				public void operationComplete(ChannelFuture future) throws Exception {
					if (future.isSuccess()) {
					} else {
						log.info(String.format("-> Streamer isError :%s", mm));
					}
				}
			});
			
			RenderLog renderLog = new RenderLog();
			renderLog.setId(uuid);
			renderLog.setAgentId(agentToken);
			renderLog.setSceneId(scene.getId());
			renderLog.setAgentGpuId(KeyUtil.getGPUId(GPUIndex));
			renderLog.setCreateUserId(scene.getCreateUserId());
			renderLog.setStatus("start");
			renderLog.setMessage("请求渲染.");
			renderLog.save();

			if (isBooted(uuid)) {
				String r = Cirrus.orders.get(uuid);
				Cirrus.orders.remove(uuid);
				JSONObject result = JSONObject.parseObject(r);
				if (result == null) {
					renderLog.setStatus("failed");
					renderLog.setMessage("渲染失败.");
					renderLog.update();
					return R.failed("渲染失败.");
				}
				if (result.getInteger("code") == 500) {
					renderLog.setStatus("failed");
					renderLog.setMessage(result.getString("message"));
					renderLog.update();
					return R.failed(result.getString("message"));
				}

				ROrder orderR = JSONObject.parseObject(result.getString("result"), ROrder.class);
				renderLog.setOnlineTime(new Date(System.currentTimeMillis()));
				renderLog.setEio(orderR.getEIO());
				renderLog.setStatus("success");
				renderLog.setMessage("渲染中.");
				renderLog.update();
				return R.ok(orderR);
			} else {
				Cirrus.orders.remove(uuid);
				renderLog.setStatus("timeout");
				renderLog.setMessage("渲染超时.");
				renderLog.update();
				return R.failed("渲染超时.");
			}
		}
	}

	private boolean isBooted(String uuid) {
		boolean isSucc = false;
		int idleTimeout = 120;
		int time = 0;
		while (true) {
			if (!StrKit.isBlank(Cirrus.orders.get(uuid))) {
				isSucc = true;
				break;
			}

			if (time > idleTimeout) {
				isSucc = false;
				break;
			}

			try {
				Thread.sleep(1000);
				time++;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		return isSucc;

	}

}
